/*
 * 窗口容器
 * @author： 黄
 * 2018.4.21
 */
import { remote, ipcRenderer } from 'electron'
import C from './common.js'
import Conf from '#/config/config.js'
import path from 'path'
import events from 'events'
/*
 * 简单说一下这个窗口的实现思路
 * 实现窗口容器思考了两种方式，第一种方式是通过在主进程中设置一个容器来存放对应的窗口，子进程通过electron自带的通讯方式和主进程容器进行通讯创建窗口
 * 优点：窗口统一归主进程创建，不依赖于外部浏览器，缺点：进程通讯异步创建窗口响应速度有待商榷
 * 第二种方式是把容器设置在浏览器localsrorage中，通过localstroage的同步属性来调用容器和保存窗口间传参
 * 优点：简单易用，同步方式，窗口间通讯方便，缺点：依赖于localstorage，主进程中不能使用
 * 目前先采用第二种方式，如果你有更好的方式可以联系我
 * 2374266244@qq.com
 */

class WindowBoxs {
    constructor() {
        this.freeWindowNum = 1 // 允许空闲的窗口数量
        // 基本的配置参数
        this.baseWindowConfig = {
            show: false,
            transparent: true,
            frame: false,
            width: 0,
            height: 0
        }
        // 初始化页面
        this.freePage = {
            model: 'shop',
            router: '/backGround'
        }
        this.isChecking = false
        this.Event = new events.EventEmitter()
        this.win = remote.getCurrentWindow()
    }

    /*
     * 打开新的空白窗口等待加载
     */
    creatFreeWindow() {
        let win = new remote.BrowserWindow(this.getWindowConfig())
        // 设置传参
        let _windowList = C.getLocalStorage('_windowList') || []
        _windowList.push({
            id: win.id,
            name: '',
            isUse: false,
            model: this.freePage.model,
            router: this.freePage.router,
            sendMsg: {},
            backMsg: {},
            fromId: ''
        })
        // 放在loadURL上面防止多进程产生的重复问题
        C.setLocalStorage('_windowList', _windowList)

        let modalPath = process.env.NODE_ENV === 'development' ?
            'http://localhost:8080/' + this.freePage.model + '.html#' + this.freePage.router :
            path.join(Conf.root, 'html/dist/', this.freePage.model) + '.html#' + this.freePage.router

        win.loadURL(modalPath)
        return win
    }

    /*
     * 适配窗口参数
     */
    getWindowConfig() {
        if (process.env.NODE_ENV === 'development') {
            this.baseWindowConfig.webPreferences = { webSecurity: false }
        }
        return this.baseWindowConfig
    }

    /*
     * 平衡空白窗口数量
     */
    checkFreeWindow() {
        // 防止重复操作定义全局变量
        let isChecking = C.getLocalStorage('_isChecking') || 0
        if (isChecking) return false
        C.setLocalStorage('_isChecking', 1)
        // 先获取是否有stroage缓存
        let _windowList = C.getLocalStorage('_windowList') || []
        // 如果有缓存，查找空余窗口是否足够，不足的话创建到给定水平（同时删掉可能多出来的窗口）
        let notUseWindowNum = 0
        _windowList.forEach(row => {
            if (!row.isUse) notUseWindowNum++
        })
        let num = this.freeWindowNum - notUseWindowNum
        if (num > 0) {
            for (var i = num; i > 0; i--) {
                this.creatFreeWindow() // 暂时循环调用，后期用延时
            }
        }
        C.setLocalStorage('_isChecking', 0)
    }

    init() {
        // 检查空窗口数量
        this.checkFreeWindow()
        // 检查是否有当前窗口名字，如果没有写进缓存，防止其他方式创建的窗口无法识别
        // this.checkThisWindow()
        // 设置窗口关闭任务（防止父页面刷新无法清空缓存）
        this.addEventListenerForWindow()
    }

    /*
     * 检查当前窗口是否在缓存中没有加入
     */
    checkThisWindow () {
        if (this.win.isVisible()) {
            let _windowInfo = this.getWindowInfo()
            if (!_windowInfo.id) {
                _windowInfo = {
                    id: this.win.id,
                    name: this.win.id,
                    isUse: true,
                    model: '',
                    router: '',
                    sendMsg: {},
                    backMsg: {},
                    fromId: ''
                }
            }
            this.setWindowInfo(_windowInfo)
            C.setLocalStorage('_winName_' + this.win.id, this.win.id)
        }
    }


    /*
     * 使用一个空白窗口
     * 暂时支持初始几个参数
     * {width,height,model,router}
     */
    getFreeWindow(option) {
        // 怎么配置参数
        // 怎么绑定事件
        // 暂时支持简单的参数（width，height，frame, transform等）
        // 判断参数是否有name和refresh属性（如果有name属性查找该name窗口是否存在，存在显示不存在新建）
        option = option || {}
        let freeWindow, freeWindowInfo
        if (option.name) {
            let winId = C.getLocalStorage('_winName_' + option.name)
            if (winId) {
                if (!option.reload) {
                    return remote.BrowserWindow.fromId(winId)
                } else {
                    freeWindow = remote.BrowserWindow.fromId(winId)
                    freeWindowInfo = C.getLocalStorage('_windowInfo_' + winId)
                }
            } else {
                freeWindowInfo = this._getFreeWindow()
                freeWindow = remote.BrowserWindow.fromId(freeWindowInfo.id)
            }
        } else {
            freeWindowInfo = this._getFreeWindow()
            freeWindow = remote.BrowserWindow.fromId(freeWindowInfo.id)
            option.name = freeWindowInfo.id
        }

        // 更新参数
        freeWindowInfo.router = option.router
        freeWindowInfo.sendMsg = option.data || {}
        freeWindowInfo.isUse = true
        freeWindowInfo.name = option.name
        freeWindowInfo.fromId = this.win.id
        // 重置窗口大小
        freeWindow.setSize(option.width, option.height)
        freeWindow.center()

        this.setUseWindow(freeWindowInfo)
        this.checkFreeWindow()
        // 发送监听事件页面内跳转（放在设置和检查窗口后面防止多进程重复问题）
        // 背景窗口还没初始化完成会导致信息无法被获取
        // 解决方案是判断窗口是否加载完成如果加载完成直接发送，如果未完成等待完成再发送
        if (freeWindow.webContents.isLoading()) {
            freeWindow.webContents.once('did-finish-load', function() {
                freeWindow.webContents.send('_changeModelPath', {
                    router: option.router,
                    windowInfo: freeWindowInfo
                })
            })
        } else {
            freeWindow.webContents.send('_changeModelPath', {
                router: option.router,
                windowInfo: freeWindowInfo
            })
        }
        return freeWindow
    }


    /*
     * 取出一个空白窗口并且返回（仅仅取出对象）
     */
    _getFreeWindow() {
        let _windowList = C.getLocalStorage('_windowList') || []
        return _windowList.filter(row => row.isUse == false).shift()
    }

    /*
     * 根据窗口id设置某一窗口为已使用
     */
    setUseWindow(freeWindowInfo) {
        let _windowList = C.getLocalStorage('_windowList') || []
        _windowList = _windowList.filter(row => row.id != freeWindowInfo.id)
        C.setLocalStorage('_windowList', _windowList)
        // 设置新的缓存（暂时不用等到show设置）
        C.setLocalStorage('_windowInfo_' + freeWindowInfo.id, freeWindowInfo)
        C.setLocalStorage('_winName_' + freeWindowInfo.name, freeWindowInfo.id)
    }


    /*
     * 给新窗口绑定close和resize事件（如果页面刷新要手动解除之前的监听事件）
     */
    addEventListenerForWindow() {
        let closeWinInfo = () => {
            let _windowInfo = this.getWindowInfo()
            // 先发送数据
            this.sendAsyncMsg({
                toWinId: _windowInfo.fromId,
                data: _windowInfo.backMsg
            })
            C.setLocalStorage('_winName_' + _windowInfo.name)
            C.setLocalStorage('_windowInfo_' + this.win.id)
            // 如果只剩下空白的窗口就关掉应用
            let allWindows = remote.BrowserWindow.getAllWindows()
            let _windowList = C.getLocalStorage('_windowList').map(row => row.id)
            let appShouldQuit = true
            for (var i = allWindows.length - 1; i >= 0; i--) {
                if (allWindows[i].id != this.win.id && _windowList.indexOf(allWindows[i].id) < 0) appShouldQuit = false
            }
            if (appShouldQuit) remote.app.quit()
            this.win = null
        }
        let setSize = () => {
            const [width, height] = remote.getCurrentWindow().getContentSize()
            for (let wc of remote.webContents.getAllWebContents()) {
                // Check if `wc` belongs to a webview in the `win` window.
                if (wc.hostWebContents &&
                    wc.hostWebContents.id === remote.getCurrentWindow().webContents.id) {
                    wc.setSize({
                        normal: {
                            width: width,
                            height: height
                        }
                    })
                }
            }
        }
        window.addEventListener('beforeunload', () => {
            this.win.removeListener('close', closeWinInfo)
            this.win.removeListener('resize', setSize)
        })
        this.win.on('close', closeWinInfo)
        this.win.on('resize', setSize)
        // 监听接收数据
        ipcRenderer.on('_windowToMsg', (event, data) => {
            this.Event.emit('_winId_' + data.fromWinId, data)
            this.Event.emit('_winOpenWin_' + data.fromWinId, data)
        })
    }


    /*
     * 获取当前页面传递过来的参数
     */
    getMsg() {
        return this.getWindowInfo().sendMsg
    }


    /*
     * 发送数据到指定窗口
     * toWinName: 窗口名称, toWinId: 窗口id, data: 发送数据
     */
    sendMsg(data) {
        if (!data.toWinId) {
            data.toWinId = C.getLocalStorage('_winName_' + data.toWinName)
        }
        // 查询是否有此窗口
        let _windowInfo = this.getWindowInfo(data.toWinId)
        _windowInfo.sendMsg = data
        this.setWindowInfo(_windowInfo, data.toWinId)
    }


    /*
     * 发送无状态立即响应的数据
     * {fromWinName: '',fromWinId: '', toWinName: '', toWinId: '', data: {}}
     */
    sendAsyncMsg(data) {
        if (!data.toWinId) {
            data.toWinId = C.getLocalStorage('_winName_' + data.toWinName)
        }
        let _windowInfo = this.getWindowInfo()
        if (_windowInfo) {
            data.fromWinName = _windowInfo.name
            data.fromWinId = _windowInfo.id
        } else {
            data.fromWinName = this.win.id
            data.fromWinId = this.win.id
        }
        remote.BrowserWindow.fromId(data.toWinId).webContents.send('_windowToMsg', {
            fromWinName: data.fromWinName,
            fromWinId: data.fromWinId,
            toWinName: data.toWinName,
            toWinId: data.toWinId,
            winId: data.winId,
            data: data.data
        })
    }

    /*
     * 接收无状态立即响应数据
     * {fromWinName: '',fromWinId: '', toWinName: '', toWinId: ''}
     */
    getAsyncMsg(data) {
        // 如果相同的事件传入把之前的事件覆盖掉
        return new Promise((resolve, reject) => {
            this.Event.on('_winId_' + data.fromWinId, (data) => {
                resolve(data.data)
            })
        })
    }


    /*
     * 通用弹窗函数
     */
    async openWin(option) {
        let win = this.getFreeWindow(option)
        win.show() 
        this.Event.removeAllListeners('_winOpenWin_' + win.id)
        return new Promise((resolve, reject) => {
            this.Event.on('_winOpenWin_' + win.id, (data) => {
                resolve(data)
            })
        })
    }


    /*
     * 关闭弹窗并且发送数据
     */
    closeWin(data) {
        if (data) {
            let _windowInfo = this.getWindowInfo()
            _windowInfo.backMsg = data
            this.setWindowInfo(_windowInfo)
        }
        this.win.close()
    }

    /*
     * 获取窗口的数据
     */
    getWindowInfo (id) {
        id = id || this.win.id
        return C.getLocalStorage('_windowInfo_' + id) || {}
    }

    /*
     * 设置窗口的数据
     */
    setWindowInfo (data, id) {
        id = id || this.win.id
        C.setLocalStorage('_windowInfo_' + id, data)
    }

    /*
     * 监听路由跳转
     */
    changePath (vm) {
        ipcRenderer.on('_changeModelPath', (event, arg) => {
            vm.$router.push({path: arg.router})
        })
    }
}

export default new WindowBoxs()